<?php

namespace Aoe\Intent\Request;

use Aoe\Attributes\Container\Dependent;
use Aoe\Core\Kernel;
use Aoe\Intent\Params;
use Aoe\Intent\Render\Stdout;
use Aoe\Intent\Response;
use Aoe\Intent\Section;
use Aoe\Util\Exception;
use Throwable;

/**
 * # 请求
 */
abstract class Request
{
    const string ROUTE_SUCCESS   = '路由解析成功';
    const string ERR_REQ         = '无效的请求${pathinfo}';
    const string ERR_RESPONSE    = '处理器没有响应${pathinfo}';
    
    /**
     * @var Params|null 运行参数
     */
    protected ?Params $params     = null;

    /**
     * @var Section|null 路由解析结果
     */
    protected(set) ?Section $section   = null {
        get {
            return $this->section;
        }
    }

    private(set) Response $response {
        get {
            return $this->response;
        }
    }

    public function __construct(#[Dependent] protected Kernel $kernel)
    {
        $this->response = new Response($this);
        $this->params = new Params([]);
    }
    /**
     * ## 发送当前请求
     */
    public function fire(): Response
    {
        $processor = $this->kernel->Router()->dispatch($this);

        // 路由解析失败信息
        if (!$processor) return $this->response->setException(
            new Exception(
                static::ERR_REQ,
                ['pathinfo' => $this->getPathinfo()]
            )
        );
        $this->kernel->recorder->Log(self::ROUTE_SUCCESS);

        $this->section = $processor;
        $this->params->append(
            $this->fill_param(),
            $this->section->pairs?->get() ?? [],
        );

        try {
            $this->kernel->getModule($this->section->module)->invoke($this);
        } catch (Throwable $e) {
            // 必须保证这里后面没有什么处理了
            return $this->response->setException($e);
        }

        if (!$this->response->completed) {
            return $this->response->setException(
                new Exception(self::ERR_RESPONSE, ['pathinfo' => $this->section->short])
            );
        }

        return $this->response;
    }

    /**
     * ## 内部通道
     * @param string $pathinfo
     * @param array $params
     * @return Response
     */
    public function channel(string $pathinfo, array $params = []): Response
    {
        return new Channel($this->kernel, $pathinfo, $params)->fire();
    }

    abstract public function getPathinfo(): string;
    abstract public function getMethod(): string;
    public function getParam($key = null, $default = null)
    {
        return $this->params->get($key, $default);
    }

    /**
     * ## 路由匹配
     *
     * @param array{
     *      module?: string,
     *      controller?: string,
     *      action?: string,
     *      method?: string,
     *      ext?: string,
     *  } $factor 匹配因子
     *
     * @return bool
     * @see Router
     *
     */
    public function fit(array $factor): bool
    {
        return true;
    }

    public function getDefaultRender(): string
    {
        return Stdout::class;
    }

    /**
     * ## fire期间自定义填充请求参数
     *
     * @return array add to intent's properties
     * @see Request
     *
     */
    protected function fill_param(): array { return []; }
}
